/*
  (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP
  [See end of file]
  $Id: AbstractTestGraph.java,v 1.1 2009/06/29 08:55:40 castagna Exp $i
 */

package com.hp.hpl.jena.graph.test;

import it.polimi.dei.dbgroup.pedigree.androjena.test.TestHelper;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


import com.hp.hpl.jena.graph.BulkUpdateHandler;
import com.hp.hpl.jena.graph.Capabilities;
import com.hp.hpl.jena.graph.Factory;
import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.graph.GraphEventManager;
import com.hp.hpl.jena.graph.GraphEvents;
import com.hp.hpl.jena.graph.GraphListener;
import com.hp.hpl.jena.graph.GraphStatisticsHandler;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.Reifier;
import com.hp.hpl.jena.graph.TransactionHandler;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.graph.query.QueryHandler;
import com.hp.hpl.jena.graph.query.SimpleQueryHandler;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.shared.Command;
import com.hp.hpl.jena.shared.JenaException;
import com.hp.hpl.jena.util.CollectionFactory;
import com.hp.hpl.jena.util.iterator.ClosableIterator;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

/**
 * AbstractTestGraph provides a bunch of basic tests for something that purports
 * to be a Graph. The abstract method getGraph must be overridden in subclasses
 * to deliver a Graph of interest.
 * 
 * @author kers
 */
public/* abstract */class AbstractTestGraph extends GraphTestBase {
	public AbstractTestGraph(String name) {
		super(name);
	}

	/**
	 * Returns a Graph to take part in the test. Must be overridden in a
	 * subclass.
	 */
	// public abstract Graph getGraph();

	public Graph getGraph() {
		return Factory.createGraphMem();
	}

	public Graph getGraphWith(String facts) {
		Graph g = getGraph();
		graphAdd(g, facts);
		return g;
	}

	public void testCloseSetsIsClosed() {
		Graph g = getGraph();
		assertFalse("unclosed Graph shouild not be isClosed()", g.isClosed());
		g.close();
		assertTrue("closed Graph should be isClosed()", g.isClosed());
	}

	/**
	 * This test case was generated by Ian and was caused by GraphMem not
	 * keeping up with changes to the find interface.
	 */
	public void testFindAndContains() {
		Graph g = getGraph();
		Node r = NodeCreateUtils.create("r"), s = NodeCreateUtils.create("s"), p = NodeCreateUtils
				.create("P");
		g.add(Triple.create(r, p, s));
		assertTrue(g.contains(r, p, Node.ANY));
		assertEquals(1, g.find(r, p, Node.ANY).toList().size());
	}

	public void testRepeatedSubjectDoesNotConceal() {
		Graph g = getGraphWith("s P o; s Q r");
		assertTrue(g.contains(triple("s P o")));
		assertTrue(g.contains(triple("s Q r")));
		assertTrue(g.contains(triple("?? P o")));
		assertTrue(g.contains(triple("?? Q r")));
		assertTrue(g.contains(triple("?? P ??")));
		assertTrue(g.contains(triple("?? Q ??")));
	}

	public void testFindByFluidTriple() {
		Graph g = getGraphWith("x y z ");
		Set<Triple> expect = tripleSet("x y z");
		assertEquals(expect, g.find(triple("?? y z")).toSet());
		assertEquals(expect, g.find(triple("x ?? z")).toSet());
		assertEquals(expect, g.find(triple("x y ??")).toSet());
	}

	public void testContainsConcrete() {
		Graph g = getGraphWith("s P o; _x _R _y; x S 0");
		assertTrue(g.contains(triple("s P o")));
		assertTrue(g.contains(triple("_x _R _y")));
		assertTrue(g.contains(triple("x S 0")));
		/* */
		assertFalse(g.contains(triple("s P Oh")));
		assertFalse(g.contains(triple("S P O")));
		assertFalse(g.contains(triple("s p o")));
		assertFalse(g.contains(triple("_x _r _y")));
		assertFalse(g.contains(triple("x S 1")));
	}

	public void testContainsFluid() {
		Graph g = getGraphWith("x R y; a P b");
		assertTrue(g.contains(triple("?? R y")));
		assertTrue(g.contains(triple("x ?? y")));
		assertTrue(g.contains(triple("x R ??")));
		assertTrue(g.contains(triple("?? P b")));
		assertTrue(g.contains(triple("a ?? b")));
		assertTrue(g.contains(triple("a P ??")));
		assertTrue(g.contains(triple("?? R y")));
		/* */
		assertFalse(g.contains(triple("?? R b")));
		assertFalse(g.contains(triple("a ?? y")));
		assertFalse(g.contains(triple("x P ??")));
		assertFalse(g.contains(triple("?? R x")));
		assertFalse(g.contains(triple("x ?? R")));
		assertFalse(g.contains(triple("a S ??")));
	}

	/**
	 * Check that contains respects by-value semantics.
	 */
	public void testContainsByValue() {
		if (getGraph().getCapabilities().handlesLiteralTyping()) { // TODO fix
																	// the
																	// adhocness
																	// of this
			Graph g1 = getGraphWith("x P '1'xsd:integer");
			assertTrue(g1.contains(triple("x P '01'xsd:int")));
			//
			Graph g2 = getGraphWith("x P '1'xsd:int");
			assertTrue(g2.contains(triple("x P '1'xsd:integer")));
			//
			Graph g3 = getGraphWith("x P '123'xsd:string");
			assertTrue(g3.contains(triple("x P '123'")));
		}
	}

	public void testMatchLanguagedLiteralCaseInsensitive() {
		Graph m = graphWith("a p 'chat'en");

		// TODO: should be Graph m = getGraphWith( "a p 'chat'en" );
		if (m.getCapabilities().handlesLiteralTyping()) {
			Node chaten = node("'chat'en"), chatEN = node("'chat'EN");
			assertDiffer(chaten, chatEN);
			assertTrue(chaten.sameValueAs(chatEN));
			assertEquals(chaten.getIndexingValue(), chatEN.getIndexingValue());
			assertEquals(1, m.find(Node.ANY, Node.ANY, chaten).toList().size());
			assertEquals(1, m.find(Node.ANY, Node.ANY, chatEN).toList().size());
		}
	}

	public void testMatchBothLanguagedLiteralsCaseInsensitive() {
		Graph m = graphWith("a p 'chat'en; a p 'chat'EN");

		// TODO: should be Graph m = getGraphWith( "a p 'chat'en; a p 'chat'EN"
		// );
		if (m.getCapabilities().handlesLiteralTyping()) {
			Node chaten = node("'chat'en"), chatEN = node("'chat'EN");
			assertDiffer(chaten, chatEN);
			assertTrue(chaten.sameValueAs(chatEN));
			assertEquals(chaten.getIndexingValue(), chatEN.getIndexingValue());
			assertEquals(2, m.find(Node.ANY, Node.ANY, chaten).toList().size());
			assertEquals(2, m.find(Node.ANY, Node.ANY, chatEN).toList().size());
		}
	}

	public void testNoMatchAgainstUnlanguagesLiteral() {
		Graph m = graphWith("a p 'chat'en; a p 'chat'");
		// TODO: should be Graph m = getGraphWith( "a p 'chat'en; a p 'chat'" );
		if (m.getCapabilities().handlesLiteralTyping()) {
			Node chaten = node("'chat'en"), chatEN = node("'chat'EN");
			assertDiffer(chaten, chatEN);
			assertTrue(chaten.sameValueAs(chatEN));
			assertEquals(chaten.getIndexingValue(), chatEN.getIndexingValue());
			assertEquals(1, m.find(Node.ANY, Node.ANY, chaten).toList().size());
			assertEquals(1, m.find(Node.ANY, Node.ANY, chatEN).toList().size());
		}
	}

	/**
	 * test isEmpty - moved from the QueryHandler code.
	 */
	public void testIsEmpty() {
		Graph g = getGraph();
		if (canBeEmpty(g)) {
			assertTrue(g.isEmpty());
			g.add(NodeCreateUtils.createTriple("S P O"));
			assertFalse(g.isEmpty());
			g.add(NodeCreateUtils.createTriple("A B C"));
			assertFalse(g.isEmpty());
			g.add(NodeCreateUtils.createTriple("S P O"));
			assertFalse(g.isEmpty());
			g.delete(NodeCreateUtils.createTriple("S P O"));
			assertFalse(g.isEmpty());
			g.delete(NodeCreateUtils.createTriple("A B C"));
			assertTrue(g.isEmpty());
		}
	}

	public void testAGraph() {
		String title = this.getClass().getName();
		Graph g = getGraph();
		int baseSize = g.size();
		graphAdd(g, "x R y; p S q; a T b");
		/* */
		assertContainsAll(title + ": simple graph", g, "x R y; p S q; a T b");
		assertEquals(title + ": size", baseSize + 3, g.size());
		graphAdd(g,
				"spindizzies lift cities; Diracs communicate instantaneously");
		assertEquals(title + ": size after adding", baseSize + 5, g.size());
		g.delete(triple("x R y"));
		g.delete(triple("a T b"));
		assertEquals(title + ": size after deleting", baseSize + 3, g.size());
		assertContainsAll(title + ": modified simple graph", g,
				"p S q; spindizzies lift cities; Diracs communicate instantaneously");
		assertOmitsAll(title + ": modified simple graph", g, "x R y; a T b");
		/* */
		ClosableIterator<Triple> it = g.find(Node.ANY, node("lift"), Node.ANY);
		assertTrue(title + ": finds some triple(s)", it.hasNext());
		assertEquals(title + ": finds a 'lift' triple",
				triple("spindizzies lift cities"), it.next());
		assertFalse(title + ": finds exactly one triple", it.hasNext());
		it.close();
	}

	// public void testStuff()
	// {
	// // testAGraph( "StoreMem", new GraphMem() );
	// // testAGraph( "StoreMemBySubject", new GraphMem() );
	// // String [] empty = new String [] {};
	// // Graph g = graphWith( "x R y; p S q; a T b" );
	// // /* */
	// // assertContainsAll( "simple graph", g, "x R y; p S q; a T b" );
	// // graphAdd( g,
	// "spindizzies lift cities; Diracs communicate instantaneously" );
	// // g.delete( triple( "x R y" ) );
	// // g.delete( triple( "a T b" ) );
	// // assertContainsAll( "modified simple graph", g,
	// "p S q; spindizzies lift cities; Diracs communicate instantaneously" );
	// // assertOmitsAll( "modified simple graph", g, "x R y; a T b" );
	// }

	/**
	 * Test that Graphs have transaction support methods, and that if they fail
	 * on some g they fail because they do not support the operation.
	 */
	public void testHasTransactions() {
		Graph g = getGraph();
		TransactionHandler th = g.getTransactionHandler();
		th.transactionsSupported();
		try {
			th.begin();
		} catch (UnsupportedOperationException x) {
		}
		try {
			th.abort();
		} catch (UnsupportedOperationException x) {
		}
		try {
			th.begin();
			th.commit();
		} catch (UnsupportedOperationException x) {
		}
		/* */
		Command cmd = new Command() {
			public Object execute() {
				return null;
			}
		};
		try {
			th.executeInTransaction(cmd);
		} catch (UnsupportedOperationException x) {
		}
	}

	public void testExecuteInTransactionCatchesThrowable() {
		Graph g = getGraph();
		TransactionHandler th = g.getTransactionHandler();
		if (th.transactionsSupported()) {
			Command cmd = new Command() {
				public Object execute() throws Error {
					throw new Error();
				}
			};
			try {
				th.executeInTransaction(cmd);
			} catch (JenaException x) {
			}
		}
	}

	static final Triple[] tripleArray = tripleArray("S P O; A R B; X Q Y");

	static final List<Triple> tripleList = Arrays
			.asList(tripleArray("i lt j; p equals q"));

	static final Triple[] setTriples = tripleArray("scissors cut paper; paper wraps stone; stone breaks scissors");

	static final Set<Triple> tripleSet = CollectionFactory
			.createHashedSet(Arrays.asList(setTriples));

	public void testBulkUpdate() {
		Graph g = getGraph();
		BulkUpdateHandler bu = g.getBulkUpdateHandler();
		Graph items = graphWith("pigs might fly; dead can dance");
		int initialSize = g.size();
		/* */
		bu.add(tripleArray);
		testContains(g, tripleArray);
		testOmits(g, tripleList);
		/* */
		bu.add(tripleList);
		testContains(g, tripleList);
		testContains(g, tripleArray);
		/* */
		bu.add(tripleSet.iterator());
		testContains(g, tripleSet.iterator());
		testContains(g, tripleList);
		testContains(g, tripleArray);
		/* */
		bu.add(items);
		testContains(g, items);
		testContains(g, tripleSet.iterator());
		testContains(g, tripleArray);
		testContains(g, tripleList);
		/* */
		bu.delete(tripleArray);
		testOmits(g, tripleArray);
		testContains(g, tripleList);
		testContains(g, tripleSet.iterator());
		testContains(g, items);
		/* */
		bu.delete(tripleSet.iterator());
		testOmits(g, tripleSet.iterator());
		testOmits(g, tripleArray);
		testContains(g, tripleList);
		testContains(g, items);
		/* */
		bu.delete(items);
		testOmits(g, tripleSet.iterator());
		testOmits(g, tripleArray);
		testContains(g, tripleList);
		testOmits(g, items);
		/* */
		bu.delete(tripleList);
		assertEquals("graph has original size", initialSize, g.size());
	}

	public void testBulkAddWithReification() {
		testBulkAddWithReification(false);
		testBulkAddWithReification(true);
	}

	public void testBulkAddWithReificationPreamble() {
		Graph g = getGraph();
		xSPO(g.getReifier());
		assertFalse(getReificationTriples(g.getReifier()).isEmpty());
	}

	public void testBulkAddWithReification(boolean withReifications) {
		Graph graphToUpdate = getGraph();
		BulkUpdateHandler bu = graphToUpdate.getBulkUpdateHandler();
		Graph graphToAdd = graphWith("pigs might fly; dead can dance");
		Reifier updatedReifier = graphToUpdate.getReifier();
		Reifier addedReifier = graphToAdd.getReifier();
		xSPOyXYZ(addedReifier);
		bu.add(graphToAdd, withReifications);
		assertIsomorphic(withReifications ? getReificationTriples(addedReifier)
				: graphWith(""), getReificationTriples(updatedReifier));
	}

	protected void xSPOyXYZ(Reifier r) {
		xSPO(r);
		r.reifyAs(NodeCreateUtils.create("y"), NodeCreateUtils
				.createTriple("X Y Z"));
	}

	protected void aABC(Reifier r) {
		r.reifyAs(NodeCreateUtils.create("a"), NodeCreateUtils
				.createTriple("A B C"));
	}

	protected void xSPO(Reifier r) {
		r.reifyAs(NodeCreateUtils.create("x"), NodeCreateUtils
				.createTriple("S P O"));
	}

	public void testRemove() {
		testRemove("?? ?? ??", "?? ?? ??");
		testRemove("S ?? ??", "S ?? ??");
		testRemove("S ?? ??", "?? P ??");
		testRemove("S ?? ??", "?? ?? O");
		testRemove("?? P ??", "S ?? ??");
		testRemove("?? P ??", "?? P ??");
		testRemove("?? P ??", "?? ?? O");
		testRemove("?? ?? O", "S ?? ??");
		testRemove("?? ?? O", "?? P ??");
		testRemove("?? ?? O", "?? ?? O");
	}

	public void testRemove(String findRemove, String findCheck) {
		Graph g = getGraphWith("S P O");
		ExtendedIterator<Triple> it = g.find(NodeCreateUtils
				.createTriple(findRemove));
		try {
			it.next();
			it.remove();
			it.close();
			assertEquals("remove with " + findRemove + ":", 0, g.size());
			assertFalse(g.contains(NodeCreateUtils.createTriple(findCheck)));
		} catch (UnsupportedOperationException e) {
			it.close();
			assertFalse(g.getCapabilities().iteratorRemoveAllowed());
		}
		it.close();
	}

	public void testBulkRemoveWithReification() {
		testBulkUpdateRemoveWithReification(true);
		testBulkUpdateRemoveWithReification(false);
	}

	public void testBulkUpdateRemoveWithReification(boolean withReifications) {
		Graph g = getGraph();
		BulkUpdateHandler bu = g.getBulkUpdateHandler();
		Graph items = graphWith("pigs might fly; dead can dance");
		Reifier gr = g.getReifier(), ir = items.getReifier();
		xSPOyXYZ(ir);
		xSPO(gr);
		aABC(gr);
		bu.delete(items, withReifications);
		Graph answer = graphWith("");
		Reifier ar = answer.getReifier();
		if (withReifications)
			aABC(ar);
		else {
			xSPO(ar);
			aABC(ar);
		}
		assertIsomorphic(getReificationTriples(ar), getReificationTriples(gr));
	}

	public void testHasCapabilities() {
		Graph g = getGraph();
		Capabilities c = g.getCapabilities();
		boolean sa = c.sizeAccurate();
		boolean aaSome = c.addAllowed();
		boolean aaAll = c.addAllowed(true);
		boolean daSome = c.deleteAllowed();
		boolean daAll = c.deleteAllowed(true);
		boolean cbe = c.canBeEmpty();
	}

	public void testFind() {
		Graph g = getGraph();
		graphAdd(g, "S P O");
		assertDiffer(new HashSet<Triple>(), g
				.find(Node.ANY, Node.ANY, Node.ANY).toSet());
		assertDiffer(new HashSet<Triple>(), g.find(Triple.ANY).toSet());
	}

	protected boolean canBeEmpty(Graph g) {
		return g.isEmpty();
	}

	public void testEventRegister() {
		Graph g = getGraph();
		GraphEventManager gem = g.getEventManager();
		assertSame(gem, gem.register(new RecordingListener()));
	}

	/**
	 * Test that we can safely unregister a listener that isn't registered.
	 */
	public void testEventUnregister() {
		getGraph().getEventManager().unregister(L);
	}

	/**
	 * Handy triple for test purposes.
	 */
	protected Triple SPO = NodeCreateUtils.createTriple("S P O");
	protected RecordingListener L = new RecordingListener();

	/**
	 * Utility: get a graph, register L with its manager, return the graph.
	 */
	protected Graph getAndRegister(GraphListener gl) {
		Graph g = getGraph();
		g.getEventManager().register(gl);
		return g;
	}

	public void testAddTriple() {
		Graph g = getAndRegister(L);
		g.add(SPO);
		L.assertHas(new Object[] { "add", g, SPO });
	}

	public void testDeleteTriple() {
		Graph g = getAndRegister(L);
		g.delete(SPO);
		L.assertHas(new Object[] { "delete", g, SPO });
	}

	public void testListSubjects() {
		Set<Node> emptySubjects = listSubjects(getGraphWith(""));
		Graph g = getGraphWith("x P y; y Q z");
		assertEquals(nodeSet("x y"), remove(listSubjects(g), emptySubjects));
		g.delete(triple("x P y"));
		assertEquals(nodeSet("y"), remove(listSubjects(g), emptySubjects));
	}

	protected Set<Node> listSubjects(Graph g) {
		return g.queryHandler().subjectsFor(Node.ANY, Node.ANY).toSet();
	}

	public void testListPredicates() {
		Set<Node> emptyPredicates = listPredicates(getGraphWith(""));
		Graph g = getGraphWith("x P y; y Q z");
		assertEquals(nodeSet("P Q"), remove(listPredicates(g), emptyPredicates));
		g.delete(triple("x P y"));
		assertEquals(nodeSet("Q"), remove(listPredicates(g), emptyPredicates));
	}

	protected Set<Node> listPredicates(Graph g) {
		return g.queryHandler().predicatesFor(Node.ANY, Node.ANY).toSet();
	}

	public void testListObjects() {
		Set<Node> emptyObjects = listObjects(getGraphWith(""));
		Graph g = getGraphWith("x P y; y Q z");
		assertEquals(nodeSet("y z"), remove(listObjects(g), emptyObjects));
		g.delete(triple("x P y"));
		assertEquals(nodeSet("z"), remove(listObjects(g), emptyObjects));
	}

	protected Set<Node> listObjects(Graph g) {
		return g.queryHandler().objectsFor(Node.ANY, Node.ANY).toSet();
	}

	/**
	 * Answer a set with all the elements of <code>A</code> except those in
	 * <code>B</code>.
	 */
	private <T> Set<T> remove(Set<T> A, Set<T> B) {
		Set<T> result = new HashSet<T>(A);
		result.removeAll(B);
		return result;
	}

	/**
	 * Ensure that triples removed by calling .remove() on the iterator returned
	 * by a find() will generate deletion notifications.
	 */
	public void testEventDeleteByFind() {
		Graph g = getAndRegister(L);
		if (g.getCapabilities().iteratorRemoveAllowed()) {
			Triple toRemove = triple("remove this triple");
			g.add(toRemove);
			ExtendedIterator<Triple> rtr = g.find(toRemove);
			assertTrue("ensure a(t least) one triple", rtr.hasNext());
			rtr.next();
			rtr.remove();
			rtr.close();
			L.assertHas(new Object[] { "add", g, toRemove, "delete", g,
					toRemove });
		}
	}

	public void testTwoListeners() {
		RecordingListener L1 = new RecordingListener();
		RecordingListener L2 = new RecordingListener();
		Graph g = getGraph();
		GraphEventManager gem = g.getEventManager();
		gem.register(L1).register(L2);
		g.add(SPO);
		L2.assertHas(new Object[] { "add", g, SPO });
		L1.assertHas(new Object[] { "add", g, SPO });
	}

	public void testUnregisterWorks() {
		Graph g = getGraph();
		GraphEventManager gem = g.getEventManager();
		gem.register(L).unregister(L);
		g.add(SPO);
		L.assertHas(new Object[] {});
	}

	public void testRegisterTwice() {
		Graph g = getAndRegister(L);
		g.getEventManager().register(L);
		g.add(SPO);
		L.assertHas(new Object[] { "add", g, SPO, "add", g, SPO });
	}

	public void testUnregisterOnce() {
		Graph g = getAndRegister(L);
		g.getEventManager().register(L).unregister(L);
		g.delete(SPO);
		L.assertHas(new Object[] { "delete", g, SPO });
	}

	public void testBulkAddArrayEvent() {
		Graph g = getAndRegister(L);
		Triple[] triples = tripleArray("x R y; a P b");
		g.getBulkUpdateHandler().add(triples);
		L.assertHas(new Object[] { "add[]", g, triples });
	}

	public void testBulkAddList() {
		Graph g = getAndRegister(L);
		List<Triple> elems = Arrays
				.asList(tripleArray("bells ring loudly; pigs might fly"));
		g.getBulkUpdateHandler().add(elems);
		L.assertHas(new Object[] { "addList", g, elems });
	}

	public void testBulkDeleteArray() {
		Graph g = getAndRegister(L);
		Triple[] triples = tripleArray("x R y; a P b");
		g.getBulkUpdateHandler().delete(triples);
		L.assertHas(new Object[] { "delete[]", g, triples });
	}

	public void testBulkDeleteList() {
		Graph g = getAndRegister(L);
		List<Triple> elems = Arrays
				.asList(tripleArray("bells ring loudly; pigs might fly"));
		g.getBulkUpdateHandler().delete(elems);
		L.assertHas(new Object[] { "deleteList", g, elems });
	}

	public void testBulkAddIterator() {
		Graph g = getAndRegister(L);
		Triple[] triples = tripleArray("I wrote this; you read that; I wrote this");
		g.getBulkUpdateHandler().add(asIterator(triples));
		L.assertHas(new Object[] { "addIterator", g, Arrays.asList(triples) });
	}

	public void testBulkDeleteIterator() {
		Graph g = getAndRegister(L);
		Triple[] triples = tripleArray("I wrote this; you read that; I wrote this");
		g.getBulkUpdateHandler().delete(asIterator(triples));
		L
				.assertHas(new Object[] { "deleteIterator", g,
						Arrays.asList(triples) });
	}

	public Iterator<Triple> asIterator(Triple[] triples) {
		return Arrays.asList(triples).iterator();
	}

	public void testBulkAddGraph() {
		Graph g = getAndRegister(L);
		Graph triples = graphWith("this type graph; I type slowly");
		g.getBulkUpdateHandler().add(triples);
		L.assertHas(new Object[] { "addGraph", g, triples });
	}

	public void testBulkDeleteGraph() {
		Graph g = getAndRegister(L);
		Graph triples = graphWith("this type graph; I type slowly");
		g.getBulkUpdateHandler().delete(triples);
		L.assertHas(new Object[] { "deleteGraph", g, triples });
	}

	public void testGeneralEvent() {
		Graph g = getAndRegister(L);
		Object value = new int[] {};
		g.getEventManager().notifyEvent(g, value);
		L.assertHas(new Object[] { "someEvent", g, value });
	}

	public void testRemoveAllEvent() {
		Graph g = getAndRegister(L);
		g.getBulkUpdateHandler().removeAll();
		L.assertHas(new Object[] { "someEvent", g, GraphEvents.removeAll });
	}

	public void testRemoveSomeEvent() {
		Graph g = getAndRegister(L);
		Node S = node("S"), P = node("??"), O = node("??");
		g.getBulkUpdateHandler().remove(S, P, O);
		Object event = GraphEvents.remove(S, P, O);
		L.assertHas(new Object[] { "someEvent", g, event });
	}

	/**
	 * Test that nodes can be found in all triple positions. However, testing
	 * for literals in subject positions is suppressed at present to avoid
	 * problems with InfGraphs which try to prevent such constructs leaking out
	 * to the RDF layer.
	 */
	public void testContainsNode() {
		Graph g = getGraph();
		graphAdd(g, "a P b; _c _Q _d; a 11 12");
		QueryHandler qh = g.queryHandler();
		assertTrue(qh.containsNode(node("a")));
		assertTrue(qh.containsNode(node("P")));
		assertTrue(qh.containsNode(node("b")));
		assertTrue(qh.containsNode(node("_c")));
		assertTrue(qh.containsNode(node("_Q")));
		assertTrue(qh.containsNode(node("_d")));
		// assertTrue( qh.containsNode( node( "10" ) ) );
		assertTrue(qh.containsNode(node("11")));
		assertTrue(qh.containsNode(node("12")));
		/* */
		assertFalse(qh.containsNode(node("x")));
		assertFalse(qh.containsNode(node("_y")));
		assertFalse(qh.containsNode(node("99")));
	}

	public void testSubjectsFor() {
		Graph g = getGraphWith("a P b; a Q c; a P d; b P x; c Q y");
		testSameSubjects(g, Node.ANY, Node.ANY);
		testSameSubjects(g, node("P"), Node.ANY);
		testSameSubjects(g, node("Q"), node("c"));
	}

	protected void testSameSubjects(Graph g, Node p, Node o) {
		Set<Node> bis = SimpleQueryHandler.subjectsFor(g, p, o).toSet();
		Set<Node> qhs = g.queryHandler().subjectsFor(p, o).toSet();
		assertEquals(bis, qhs);
	}

	public void testListSubjectsNoRemove() {
		Graph g = getGraphWith("a P b; b Q c; c R a");
		Iterator<Node> it = g.queryHandler().subjectsFor(Node.ANY, Node.ANY);
		it.next();
		try {
			it.remove();
			fail("listSubjects for " + g.getClass()
					+ " should not support .remove()");
		} catch (UnsupportedOperationException e) {
			pass();
		}
	}

	public void testObjectsFor() {
		Graph g = getGraphWith("b P a; c Q a; d P a; x P b; y Q c");
		testSameObjects(g, Node.ANY, Node.ANY);
		testSameObjects(g, node("P"), Node.ANY);
		testSameObjects(g, node("Q"), node("c"));
	}

	protected void testSameObjects(Graph g, Node s, Node p) {
		Set<Node> bis = SimpleQueryHandler.objectsFor(g, s, p).toSet();
		Set<Node> qhs = g.queryHandler().objectsFor(s, p).toSet();
		assertEquals(bis, qhs);
	}

	public void testListObjectsNoRemove() {
		Graph g = getGraphWith("a P b; b Q c; c R a");
		Iterator<Node> it = g.queryHandler().objectsFor(Node.ANY, Node.ANY);
		it.next();
		try {
			it.remove();
			fail("listObjects for " + g.getClass()
					+ " should not support .remove()");
		} catch (UnsupportedOperationException e) {
			pass();
		}
	}

	public void testListObjectNoDuplicates() {
		Graph g = getGraphWith("a P 1; b P 1");
		int count = 0;
		Node one = node("1");
		Iterator<Node> it = g.queryHandler().objectsFor(Node.ANY, Node.ANY);
		while (it.hasNext())
			if (it.next().equals(one))
				count += 1;
		assertEquals(1, count);
	}

	public void testPredicatesFor() {
		Graph g = getGraphWith("a P b; c Q d; e R f; g P b; h Q i");
		testSamePredicates(g, Node.ANY, Node.ANY);
		testSamePredicates(g, Node.ANY, node("b"));
		testSamePredicates(g, node("g"), Node.ANY);
		testSamePredicates(g, node("e"), node("f"));
	}

	protected void testSamePredicates(Graph g, Node s, Node o) {
		Set<Node> bis = SimpleQueryHandler.predicatesFor(g, s, o).toSet();
		Set<Node> qhs = g.queryHandler().predicatesFor(s, o).toSet();
		assertEquals(bis, qhs);
	}

	public void testListPredicatesNoRemove() {
		Graph g = getGraphWith("a P b; b Q c; c R a");
		Iterator<Node> it = g.queryHandler().predicatesFor(Node.ANY, Node.ANY);
		it.next();
		try {
			it.remove();
			fail("listPredicates for " + g.getClass()
					+ " should not support .remove()");
		} catch (UnsupportedOperationException e) {
			pass();
		}
	}

	public void testRemoveAll() {
		testRemoveAll("");
		testRemoveAll("a R b");
		testRemoveAll("c S d; e:ff GGG hhhh; _i J 27; Ell Em 'en'");
	}

	public void testRemoveAll(String triples) {
		Graph g = getGraph();
		graphAdd(g, triples);
		g.getBulkUpdateHandler().removeAll();
		assertTrue(g.isEmpty());
	}

	public void testGetStatisticsHandler() {
		Graph g = getGraph();
		GraphStatisticsHandler h = g.getStatisticsHandler();
		assertSame(h, g.getStatisticsHandler());
	}

	/**
	 * Test cases for RemoveSPO(); each entry is a triple (add, remove, result).
	 * <ul>
	 * <li>add - the triples to add to the graph to start with
	 * <li>remove - the pattern to use in the removal
	 * <li>result - the triples that should remain in the graph
	 * </ul>
	 */
	protected String[][] cases = { { "x R y", "x R y", "" },
			{ "x R y; a P b", "x R y", "a P b" },
			{ "x R y; a P b", "?? R y", "a P b" },
			{ "x R y; a P b", "x R ??", "a P b" },
			{ "x R y; a P b", "x ?? y", "a P b" },
			{ "x R y; a P b", "?? ?? ??", "" },
			{ "x R y; a P b; c P d", "?? P ??", "x R y" },
			{ "x R y; a P b; x S y", "x ?? ??", "a P b" }, };

	/**
	 * Test that remove(s, p, o) works, in the presence of inferencing graphs
	 * that mean emptyness isn't available. This is why we go round the houses
	 * and test that expected ~= initialContent + addedStuff - removed -
	 * initialContent.
	 */
	public void testRemoveSPO() {
		for (int i = 0; i < cases.length; i += 1)
			for (int j = 0; j < 3; j += 1) {
				Graph content = getGraph();
				Graph baseContent = copy(content);
				graphAdd(content, cases[i][0]);
				Triple remove = triple(cases[i][1]);
				Graph expected = graphWith(cases[i][2]);
				content.getBulkUpdateHandler().remove(remove.getSubject(),
						remove.getPredicate(), remove.getObject());
				Graph finalContent = remove(copy(content), baseContent);
				assertIsomorphic(cases[i][1], expected, finalContent);
			}
	}

	/**
	 * testIsomorphism from file data
	 * 
	 * @throws FileNotFoundException
	 */
	public void testIsomorphismFile() throws IOException {
		testIsomorphismXMLFile(1, true);
		testIsomorphismXMLFile(2, true);
		testIsomorphismXMLFile(3, true);
		testIsomorphismXMLFile(4, true);
		testIsomorphismXMLFile(5, false);
		testIsomorphismXMLFile(6, false);
		testIsomorphismNTripleFile(7, true);
		testIsomorphismNTripleFile(8, false);

	}

	private void testIsomorphismNTripleFile(int i, boolean result)
			throws IOException {
		testIsomorphismFile(i, "N-TRIPLE", "nt", result);
	}

	private void testIsomorphismXMLFile(int i, boolean result)
			throws IOException {
		testIsomorphismFile(i, "RDF/XML", "rdf", result);

	}

	String filebase = "testing/regression/testModelEquals/";

	private void testIsomorphismFile(int n, String lang, String suffix,
			boolean result) throws IOException {

		Graph g1 = getGraph();
		Graph g2 = getGraph();
		Model m1 = ModelFactory.createModelForGraph(g1);
		Model m2 = ModelFactory.createModelForGraph(g2);
		// ANDROID: changed to use classloader and a reader
		// m1.read(
		// new FileInputStream(filebase + Integer.toString(n) + "-1."+suffix),
		// "http://www.example.org/",lang);
		// m2.read(
		// new FileInputStream(filebase + Integer.toString(n) + "-2."+suffix),
		// "http://www.example.org/",lang);
		m1.read(new BufferedReader(new InputStreamReader(TestHelper.openResource(filebase
				+ Integer.toString(n) + "-1." + suffix))),
				"http://www.example.org/", lang);
		m2.read(new BufferedReader(new InputStreamReader(TestHelper.openResource(filebase
				+ Integer.toString(n) + "-2." + suffix))),
				"http://www.example.org/", lang);

		boolean rslt = g1.isIsomorphicWith(g2) == result;
		if (!rslt) {
			System.out.println("g1:");
			TestHelper.dumpModel(m1);
//			m1.write(System.out, "N-TRIPLE");
			System.out.println("g2:");
			TestHelper.dumpModel(m2);
//			m2.write(System.out, "N-TRIPLE");
		}
		assertTrue("Isomorphism test failed", rslt);
	}

	protected void add(Graph toUpdate, Graph toAdd) {
		toUpdate.getBulkUpdateHandler().add(toAdd);
	}

	protected Graph remove(Graph toUpdate, Graph toRemove) {
		toUpdate.getBulkUpdateHandler().delete(toRemove);
		return toUpdate;
	}

	protected Graph copy(Graph g) {
		Graph result = Factory.createDefaultGraph();
		result.getBulkUpdateHandler().add(g);
		return result;
	}

	protected Graph getClosed() {
		Graph result = getGraph();
		result.close();
		return result;
	}

	// public void testClosedDelete()
	// {
	// try { getClosed().delete( triple( "x R y" ) ); fail( "delete when closed"
	// ); }
	// catch (ClosedException c) { /* as required */ }
	// }
	//        
	// public void testClosedAdd()
	// {
	// try { getClosed().add( triple( "x R y" ) ); fail( "add when closed" ); }
	// catch (ClosedException c) { /* as required */ }
	// }
	//        
	// public void testClosedContainsTriple()
	// {
	// try { getClosed().contains( triple( "x R y" ) ); fail(
	// "contains[triple] when closed" ); }
	// catch (ClosedException c) { /* as required */ }
	// }
	//        
	// public void testClosedContainsSPO()
	// {
	// Node a = Node.ANY;
	// try { getClosed().contains( a, a, a ); fail( "contains[SPO] when closed"
	// ); }
	// catch (ClosedException c) { /* as required */ }
	// }
	//        
	// public void testClosedFindTriple()
	// {
	// try { getClosed().find( triple( "x R y" ) ); fail(
	// "find [triple] when closed" ); }
	// catch (ClosedException c) { /* as required */ }
	// }
	//        
	// public void testClosedFindSPO()
	// {
	// Node a = Node.ANY;
	// try { getClosed().find( a, a, a ); fail( "find[SPO] when closed" ); }
	// catch (ClosedException c) { /* as required */ }
	// }
	//        
	// public void testClosedSize()
	// {
	// try { getClosed().size(); fail( "size when closed (" + this.getClass() +
	// ")" ); }
	// catch (ClosedException c) { /* as required */ }
	// }

}

/*
 * (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard
 * Development Company, LP All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */